package com.yycx.module.file.provider.oss.client;


import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.yycx.common.base.entity.EntityMap;
import com.yycx.common.base.service.WebSocketMsgService;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.common.utils.ApiAssert;
import com.yycx.common.utils.RedisUtils;
import com.yycx.module.file.client.entity.SysFile;
import com.yycx.module.file.client.vo.OssSetting;
import com.yycx.module.file.provider.enums.FileGroupEnum;
import com.yycx.module.file.provider.enums.StoreTypeEnum;
import com.yycx.module.file.provider.oss.FileUploadUtil;
import com.yycx.module.file.provider.service.OssUploadService;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Base64;
import java.util.Iterator;

/**
 * 本地文件上传
 *
 * @author zyf
 */
@Component("LOCAL_OSS")
@Slf4j
public class LocalUploadClient implements OssUploadService {

    private RedisUtils redisUtils;
    @Autowired(required = false)
    private WebSocketMsgService webSocketMsgService;

    /**
     * 图片超过多少生成缩略图
     */
    private final static Integer FILE_SIZE = 51200;


    @Override
    public OssSetting getOssSetting() {
        String v = redisUtils.getConfig(StoreTypeEnum.LOCAL_OSS.name());
        if (FlymeUtils.isNotEmpty(v)) {
            return JSONUtil.toBean(v, OssSetting.class);
        }
        return null;
    }

    @Override
    public String getBaseOssPath() {
        OssSetting ossSetting = getOssSetting();
        return ossSetting.getHttp() + ossSetting.getDomain();
    }

    @Override
    public String getOssPath(String fileBasePath, String fileName) {
        return getBaseOssPath() + fileBasePath + fileName;
    }

    @Override
    public String getLocalPath(String fileBasePath, String fileKey) {
        return getOssSetting().getFilePath() + "/" + fileBasePath+fileKey;
    }


    @Override
    public String upload(String filePath, String fileName, SysFile sysFile, Long uid, String fileBasePath, String fileKey, Long userId,EntityMap params) {
        String base64Img = sysFile.getBase64Img();
        InputStream inputStream = sysFile.getSerializableStream().getInputStream();
        String localPath = "";
        String fileUrl = "";
        if (FlymeUtils.isNotEmpty(base64Img)) {
            //localPath = cutImageByBase64(multipartFile, fileName, sysFile, base64Img);
        } else {
            OssSetting os = getOssSetting();
            //文件夹路径
            String dirPath = os.getFilePath() + "/" + fileBasePath;
            //文件全路径
            localPath = dirPath + fileKey;
            //创建文件
            File f = createFile(dirPath, fileKey);
            String basePath = getBaseOssPath();
            Long fileId = sysFile.getFileId();
            fileUrl = basePath + "/" + fileId;
            try {
                Integer fileSize=inputStream.available();
                //multipartFile.transferTo(f);
                writeToLocal(localPath,inputStream);
                if (FlymeUtils.isNotEmpty(sysFile)) {
                    String smallPath = "";
                    sysFile.setHasSmallOssPath(false);
                    Integer fileGroup = sysFile.getFileGroup();
                    if (FlymeUtils.isNotEmpty(fileGroup) && fileGroup.equals(FileGroupEnum.Image.getValue())) {
                        fileUrl = basePath + "/image/" + fileId;
                        if (fileSize > FILE_SIZE) {
                            smallPath = dirPath + "small";
                            //生成缩略图文件夹
                            mkdirs(smallPath);
                            //缩略图文件路径
                            smallPath += "/" + fileName;
                            //生成缩略图
                            Thumbnails.of(localPath).scale(0.4f).toFile(smallPath);
                            sysFile.setSmallPath(smallPath);
                        }
                    }
                    sysFile.setLocalPath(localPath);
                    sysFile.setFileUrl(fileUrl);
                }
            } catch (IOException e) {
                log.error(e.toString());
                ApiAssert.failure("文件上传错误");
            }
        }
        return localPath;
    }

    /**
     * 将InputStream写入本地文件
     * @param destination 写入本地目录
     * @param input    输入流
     * @throws IOException
     */
    private static void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
        while ((index = input.read(bytes)) != -1) {
            downloadFile.write(bytes, 0, index);
            downloadFile.flush();
        }
        downloadFile.close();
        input.close();
    }


    /**
     * 创建文件
     */
    private File createFile(String path, String fileName) {
        File dir = new File(path);
        if (!dir.exists()) {
            //创建文件夹
            dir.mkdirs();
        }
        File f = new File(path + "/" + fileName);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        ApiAssert.isFalse("文件名已存在", f.exists());
        return f;
    }

    /**
     * 创建文件夹
     *
     * @param path
     */
    private void mkdirs(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }


    public LocalUploadClient(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    /**
     * @param file
     * @param key
     * @param sysFile
     * @param base64Img
     * @描述 —— 裁剪图片
     */
    public String cutImageByBase64(MultipartFile file, String key, SysFile sysFile, String base64Img) {
        OssSetting os = getOssSetting();
        String day = DateUtil.format(DateUtil.date(), "yyyyMMdd");
        String path = os.getFilePath() + "/" + day + "/" + sysFile.getFileId();
        String filePath = path + "/" + key;
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File f = new File(filePath);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        try {
            file.transferTo(f);
            String smallPath = os.getFilePath() + "/" + day + "/" + sysFile.getFileId() + "/" + "small" + "/" + key;
            Base64ToImage(base64Img, smallPath);
            sysFile.setLocalPath(filePath).setSmallPath(smallPath);
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
            return "失败";
        }
    }

    /**
     * base64字符串转换成图片
     *
     * @param imgStr      base64字符串
     * @param imgFilePath 图片存放路径
     * @return
     */
    public static boolean Base64ToImage(String imgStr, String imgFilePath) {
        if (FlymeUtils.isEmpty(imgStr)) {
            return false;
        }
        imgStr = imgStr.substring(imgStr.indexOf(",") + 1);
        Base64.Decoder decoder = Base64.getMimeDecoder();
        try {
            // Base64解码
            byte[] b = decoder.decode(imgStr);
            for (int i = 0; i < b.length; ++i) {
                // 调整异常数据
                if (b[i] < 0) {
                    b[i] += 256;
                }
            }
            //文件夹不存在则自动创建
            File tempFile = new File(imgFilePath);
            if (!tempFile.getParentFile().exists()) {
                tempFile.getParentFile().mkdirs();
            }

            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(b);
            out.flush();
            out.close();

            return true;
        } catch (Exception e) {
            return false;
        }

    }

    /**
     * @param file
     * @param key
     * @param sysFile
     * @param x
     * @param y
     * @param width
     * @param height
     * @描述 —— 裁剪图片
     */
    public String cutImage(MultipartFile file, String key, SysFile sysFile, int x, int y, int width, int height) {

        String day = DateUtil.format(DateUtil.date(), "yyyyMMdd");
        String path = getOssSetting().getFilePath() + "/" + day + "/" + sysFile.getFileId();
        String filePath = path + "/" + key;
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File f = new File(filePath);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        FileInputStream is = null;
        ImageInputStream iis = null;
        try {
            file.transferTo(f);
            // 读取图片文件
            is = new FileInputStream(filePath);
            // 获取文件格式
            String ext = FileUploadUtil.getExtend(f.getName());
            // ImageReader解码指定格式
            Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(ext);
            ImageReader reader = it.next();
            // 获取图片流
            iis = ImageIO.createImageInputStream(is);
            // 输入源中的图像将只按顺序读取
            reader.setInput(iis, true);
            // 描述如何对流进行解码
            ImageReadParam param = reader.getDefaultReadParam();
            // 图片裁剪区域
            Rectangle rect = new Rectangle(x, y, width, height);
            // 提供一个 BufferedImage，将其用作解码像素数据的目标
            param.setSourceRegion(rect);
            // 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象
            BufferedImage bi = reader.read(0, param);
            // 保存新图片

            //压缩图片
            String smallPath = getOssSetting().getFilePath() + "/" + day + "/" + sysFile.getFileId() + "/" + "small";
            File smallDir = new File(smallPath + "/" + key);
            if (!smallDir.exists()) {
                smallDir.mkdirs();
            }
            ImageIO.write(bi, ext, smallDir);
            sysFile.setLocalPath(filePath).setSmallPath(smallPath + "/" + key);
            return filePath;

        } catch (Exception e) {
            e.printStackTrace();
            return "失败";
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (iis != null) {
                    iis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                return "失败";
            }
        }
    }
}
